#include "serversocket.h"
#include "clientsocket.h"

ServerSocket::ServerSocket(QObject *parent) :
    QObject(parent)
{
    iUdpBroadcastSocket = 0;
    iTcpServer = 0;
    iTimerId = -1;
    udpOnly = false;
    iWantedToStart = false;

    QTimer::singleShot(2000,this,SLOT(startNetwork()));

}


void ServerSocket::startNetwork()
{
    //writeLog("Finds networks");

    // TODO: Should clinetsocket.cpp have also QNetworkConfigurationManager?
    iNetworkManager = new QNetworkConfigurationManager(this);
/*
    QObject::connect(iNetworkManager, SIGNAL(updateCompleted()), this, SLOT(networkUpdated()));
    iNetworkManager->updateConfigurations();

#ifdef Q_WS_SIMULATOR
    networkUpdated();
#endif
*/

    QNetworkConfiguration cfg = iNetworkManager->defaultConfiguration();
    iNetworkSession = new QNetworkSession(cfg,this);
    iNetworkSession->open();
    iNetworkSession->waitForOpened();
    iServerAddress = this->findServerAddress();
    if (iWantedToStart) {
        start();
    }

}


void ServerSocket::networkUpdated()
{
    if (!iNetworkSession) {
//        QList<QNetworkConfiguration> configs = iNetworkManager->allConfigurations(QNetworkConfiguration::Discovered);
        QList<QNetworkConfiguration> configs = iNetworkManager->allConfigurations();

        foreach(QNetworkConfiguration conf, configs) {

            qDebug() << "-----------NET----------";
            qDebug() << "Name: " << conf.name();
            qDebug() << "Bearer type: " << conf.bearerTypeName();
            qDebug() << "Bearer type: " << conf.type();
            qDebug() << "State: " << conf.state();
            qDebug() << "Valid: " << conf.isValid();
            qDebug() << "Purpose: " << conf.purpose();
            qDebug() << "Identifier: " << conf.identifier();


#if defined Q_OS_SYMBIAN
//            if ( conf.isValid() && conf.purpose() == QNetworkConfiguration::PublicPurpose &&
            if ( conf.isValid() &&
                 conf.bearerType() == QNetworkConfiguration::BearerWLAN) {
#elif defined Q_WS_SIMULATOR
            if ( conf.isValid() &&
                 conf.bearerType() == QNetworkConfiguration::BearerEthernet ) {
#endif
                iNetworkSession = new QNetworkSession(conf, this);
                writeLog("Try to open: "+conf.name());
                iNetworkSession->open();
                iNetworkSession->waitForOpened(10000);
                if(iNetworkSession->isOpen()) {
                    iServerAddress = this->findServerAddress();
                    if (iWantedToStart) {
                        start();
                        break;
                    }
                } else {
                    writeLog("Could not open: "+conf.name());
                    delete iNetworkSession;
                    iNetworkSession = 0;
                }

            }
        }
    }

    if(!iNetworkSession) {
        // TODO: no connection
        writeLog("NOT connected to network");
    }

}

ServerSocket::~ServerSocket()
{
    stop();
}

bool ServerSocket::UdpOnly()
{
    return udpOnly;
}

void ServerSocket::setUdpOnly(bool udb)
{
    udpOnly = udb;
}


void ServerSocket::start()
{
    //writeLog("Try to start Server");
    if (!iNetworkSession) {
        // Network not open. Open later when network exists at networkUpdated()
        iWantedToStart = true;
        return;
    }

    if (!iTcpServer) {
        // Start TCP server
        if (iNetworkSession && iNetworkSession->isOpen()) {
            // Server for listening Client
            // ** TCP **
            iTcpServer = new QTcpServer(this);
            if (!udpOnly && iTcpServer->listen(QHostAddress::Any, SERVER_PORT)) {
                QObject::connect(iTcpServer, SIGNAL(newConnection()), this, SLOT(establishClientConnection()));
                writeLog(QString("Server listening on port: %1").arg(iTcpServer->serverPort()));
                // Start broadcasting server IP periodically to Clients
                if (!iUdpBroadcastSocket) {
                    iUdpBroadcastSocket = new QUdpSocket(this);
                    iTimerId = this->startTimer(4000); // Broadcast IP
                }
                writeLog("Started");
            }
            else if (udpOnly) {
                // ** UDP  **
                // Start broadcasting server IP periodically to Clients
                if (!iUdpBroadcastSocket) {
                    iUdpBroadcastSocket = new QUdpSocket(this);
                    QObject::connect(iUdpBroadcastSocket, SIGNAL(readyRead()),this, SLOT(udpGetData()));
                    iTimerId = this->startTimer(4000); // Broadcast hello
                }
            }
        } else {
            writeLog("NOT started");
        }
    } else {
        writeLog("Already started");
    }
}


void ServerSocket::stop()
{
    this->killTimer(iTimerId);
    iTimerId = -1;

    if (iTcpServer) {
        iTcpServer->close();
        //delete iTcpServer;
        iTcpServer = 0;
    }

    if (iUdpBroadcastSocket) {
        iUdpBroadcastSocket->close();
        //delete iUdpBroadcastSocket;
        iUdpBroadcastSocket = 0;
    }

    if (iNetworkSession) {
        iNetworkSession->close();
        //delete iNetworkSession;
        iNetworkSession = 0;
    }
}

void ServerSocket::writeLog(QString log)
{
    log = log.prepend("[Server] ");
    qDebug() << log;
    emit serverLog(log);
}

void ServerSocket::timerEvent(QTimerEvent* e)
{
    if (iTimerId == e->timerId()) {
        if (udpOnly) {
            sendToClient("UDP Hello from Server");
        } else {
            broadcast();
        }
    }
}

QString ServerSocket::findServerAddress()
{
    QString ret;
    if (iNetworkSession && iNetworkSession->isOpen()) {
        // Go through all interfaces and add all IPs for interfaces that
        // can broadcast and are not local.
        foreach (QNetworkInterface qni, QNetworkInterface::allInterfaces() ) {
            if (!qni.addressEntries().isEmpty() && qni.isValid()) {
                QNetworkInterface::InterfaceFlags fl = qni.flags();
                if ( fl & QNetworkInterface::IsUp &&
                     fl & QNetworkInterface::IsRunning &&
                     fl & QNetworkInterface::CanBroadcast &&
                     fl & QNetworkInterface::CanMulticast && // TODO: ?????
                     !(fl & QNetworkInterface::IsLoopBack) ) {

                    ret = qni.addressEntries().at(0).ip().toString();
                    foreach(QNetworkAddressEntry e, qni.addressEntries())
                    {
                        qDebug() << e.ip().toString();
                        ret = e.ip().toString();
                    }
                    return ret;
                }
            }
        }
    }
    return ret;
}

void ServerSocket::broadcast()
{
    QString msg = QString(SERVER_ID) + "@"+ iServerAddress;
    QByteArray datagram;
    datagram.append(msg);
    iUdpBroadcastSocket->writeDatagram(datagram.data(), datagram.size(),
                                       QHostAddress::Broadcast, SERVER_PORT);
    writeLog("Broadcast: "+msg);
}

void ServerSocket::establishClientConnection()
{
    writeLog("Client tries to connect to the Server");
    iClientCommSocket = iTcpServer->nextPendingConnection();
    if (iClientCommSocket) {
        writeLog(QString("Client address: %1").arg(iClientCommSocket->peerAddress().toString()));
        writeLog(QString("Client port: %1").arg(iClientCommSocket->peerPort()));
        emit clientConnected(iClientCommSocket->peerAddress().toString(),iClientCommSocket->peerPort());
        QObject::connect(iClientCommSocket, SIGNAL(readyRead()), this, SLOT(readClientTcpData()));

        // Stop broadcasting
        this->killTimer(iTimerId);
        iTimerId = -1;

        sendToClient("Hello from Server");

    } else {
        writeLog("No pending connections");
    }
}

void ServerSocket::readClientTcpData()
{
    QString msg(iClientCommSocket->readAll());
    writeLog("From Client: "+msg);
}

void ServerSocket::udpGetData()
{
    while (iUdpBroadcastSocket->hasPendingDatagrams()) {
        QByteArray datagram;
        datagram.resize(iUdpBroadcastSocket->pendingDatagramSize());
        iUdpBroadcastSocket->readDatagram(datagram.data(), datagram.size());

        if (udpOnly && datagram.startsWith(CLIENT_ID)) {
            QString data(datagram);
            writeLog("From UDP: "+data);
        }
        break;
    }
}

void ServerSocket::sendToClient(QString msg)
{
    QByteArray datagram;
    if (udpOnly) {
        datagram.append(QString(SERVER_ID));
        datagram.append(msg);
        iUdpBroadcastSocket->writeDatagram(datagram.data(), datagram.size(),
                                           QHostAddress::Broadcast, SERVER_PORT);
    } else {
        datagram.append(msg);
        iClientCommSocket->write(datagram);
    }
    writeLog("To Client: "+msg);
}
